Pay-as-you-go audio & video transcription with diarization + AI. Transcribe, podcast feeds, SRT/VTT export, search – BYO key or hosted OAuth connector. Never trains on your audio.
Transcribe any audio, video, or podcast programmatically – over the REST API, or as an MCP server so AI agents transcribe on their own. Async jobs with polling or webhooks, and native podcast (RSS / Apple) support.
Want the short version? See the MCP server page. Full setup below.
Let an AI agent (Claude, ChatGPT, Cursor, and other MCP clients) transcribe on its own – hours-long files, diarization, correctly-timed SRT/VTT, paste-a-link, whole podcast feeds, and search across a transcript – over the Model Context Protocol. Two ways to connect:
1. Hosted connector – no API key
Point a remote MCP connector at https://pepys.co/api/mcp. The agent signs in to Pepys once (OAuth); transcription bills to your credits. In Claude: Settings → Connectors → Add custom connector. Best for Claude and ChatGPT.
2. Local server – your own key
Run the published pepys-mcpserver with your API key – best for local dev agents. Add to your client's MCP config:
Both expose the same tools – transcribe, get_transcription, upload_file, export_transcript, transcribe_podcast_feed, search_transcript, and get_credit_balance. The 60 free minutes on signup apply; diarization, whole-feed batch, and word-level export unlock with any one-time purchase.
Usage draws from your account credit balance (1 credit = 1 minute). Enable auto-reload for unattended jobs so they don't pause at a zero balance.
Create a transcription
POST/api/v1/transcriptions
Pass exactly one source: a remote url (audio/video file, podcast RSS or Apple link, or a social link), a media_ref from a direct upload, or a public blob_url.
A podcast is an RSS feed; each episode's audio is its <enclosure>. Pass a feed URL (or an Apple Podcasts link) as urlto transcribe its latest episode, or list episodes first and transcribe a specific one's audio_url.
Register an endpoint under Settings → Webhooks (or POST /api/v1/webhooks) to receive a signed POST on transcription.completed and transcription.failed – no polling.
POST https://your-server.com/webhooks/pepysPepys-Event-Type: transcription.completed
Pepys-Event-Id: <jobId>.done
Pepys-Signature: t=1718800000,v1=<hmac-sha256-hex>
Recompute HMAC-SHA256 over {timestamp}.{rawBody} with your endpoint secret and constant-time compare against the v1 value.
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody, header, secret) {
const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = createHmac("sha256", secret).update(${t}.${rawBody}).digest("hex");
const ok = v1.length === expected.length &&
timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
if (!ok) throw new Error("bad signature");
// Optional: reject if Math.abs(Date.now()/1000 - Number(t)) > 300 (replay window).
return JSON.parse(rawBody);
}
Respond 2xx within 10s. Non-2xx / timeouts retry with backoff (up to 6 attempts). The payload carries a summary; fetch the full transcript via GET /api/v1/transcriptions/{id}.
Errors & limits
401 – missing/invalid key.
402 – out of credits (top up / enable auto-reload).
400 / 422 – bad params or an unresolvable link.
429 – rate limited (a Retry-After header is included).
Clips for social are capped at 90s; transcription length follows your plan limits.
",{}]}]},"$undefined","$undefined",16]}],[["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/0ajpl7ai_4m_o.css?dpl=dpl_8xFFCU88gTSYLbNfJx9g8HgaifDc","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/_next/static/chunks/031xid4pf_y1u.js?dpl=dpl_8xFFCU88gTSYLbNfJx9g8HgaifDc","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/_next/static/chunks/39zy3e3tnooms.js?dpl=dpl_8xFFCU88gTSYLbNfJx9g8HgaifDc","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/_next/static/chunks/3mmmwto8crmtk.js?dpl=dpl_8xFFCU88gTSYLbNfJx9g8HgaifDc","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/_next/static/chunks/0svdiea4jl5ns.js?dpl=dpl_8xFFCU88gTSYLbNfJx9g8HgaifDc","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","className":"newsreader_7c3eef01-module__hj8jBq__variable hanken_grotesk_50c767ee-module__XAFBCq__variable ibm_plex_mono_fb20554a-module__HYs5oG__variable h-full antialiased","children":["$","body",null,{"className":"min-h-full flex flex-col","children":[[["$","$L4",null,{"src":"https://www.googletagmanager.com/gtag/js?id=G-0SVE88HBL0","strategy":"afterInteractive"}],["$","$L4",null,{"id":"ga-gtag","strategy":"afterInteractive","children":"window.dataLayer = window.dataLayer || [];\nfunction gtag(){dataLayer.push(arguments);}\ngtag('js', new Date());\ngtag('config', 'G-0SVE88HBL0');"}]],["$","script",null,{"type":"application/ld+json","dangerouslySetInnerHTML":{"__html":"{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://pepys.co/#organization\",\"name\":\"Pepys\",\"legalName\":\"KMF Ventures LLC\",\"url\":\"https://pepys.co/\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://pepys.co/brand/pepys-mark-forest-1024.png\",\"caption\":\"Pepys logo\"},\"description\":\"Pepys is a pay-as-you-go audio and video transcription tool with built-in AI summaries, speaker labels, timestamps, and 99+ language support. Credits never expire – no subscription.\",\"contactPoint\":{\"@type\":\"ContactPoint\",\"email\":\"contact@pepys.co\",\"contactType\":\"customer support\",\"availableLanguage\":[\"English\"]}},{\"@type\":\"WebSite\",\"@id\":\"https://pepys.co/#website\",\"name\":\"Pepys\",\"url\":\"https://pepys.co/\",\"inLanguage\":\"en\",\"publisher\":{\"@id\":\"https://pepys.co/#organization\"}}]}"}}],["$","svg",null,{"aria-hidden":"true","className":"pointer-events-none fixed inset-0 -z-10 h-full w-full opacity-[0.25] dark:opacity-[0.07]","xmlns":"http://www.w3.org/2000/svg","children":[["$","filter",null,{"id":"ys-grain","children":[["$","feTurbulence",null,{"type":"fractalNoise","baseFrequency":"0.8","numOctaves":"2","stitchTiles":"stitch"}],"$L5"]}],"$L6"]}],"$L7"]}]}]]}],{"children":["$L8",{"children":["$L9",{},null,false,null]},null,false,"$@a"]},null,false,null]},null,false,"$@a"],"$Lb",false]],"m":"$undefined","G":["$c",[]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined"}\n"]) Ventures LLC\",\"url\":\"https://pepys.co/\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://pepys.co/brand/pepys-mark-forest-1024.png\",\"caption\":\"Pepys logo\"},\"description\":\"Pepys is a pay-as-you-go audio and video transcription tool with built-in AI summaries, speaker labels, timestamps, and 99+ language support. Credits never expire – no subscription.\",\"contactPoint\":{\"@type\":\"ContactPoint\",\"email\":\"contact@pepys.co\",\"contactType\":\"customer support\",\"availableLanguage\":[\"English\"]}}"}}],["$","script",null,{"type":"application/ld+json","dangerouslySetInnerHTML":{"__html":"{\"@type\":\"WebSite\",\"@id\":\"https://pepys.co/#website\",\"name\":\"Pepys\",\"url\":\"https://pepys.co/\",\"inLanguage\":\"en\",\"publisher\":{\"@id\":\"https://pepys.co/#organization\"}}"}}],["$","$L2",null,{"parallelRouterKey":"children","error":"$e","errorStyles":[],"errorScripts":[["$","script","script-0",{"src":"/next/static/chunks/404oer0cnlm54.js?dpl=dpl_8xFFCU88gTSYLbNfJx9g8HgaifDc","async":true}]],"template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","$Lf",null,{}],["$","main",null,{"className":"flex flex-1 flex-col","children":["$","section",null,{"className":"mx-auto flex w-full max-w-2xl flex-1 flex-col items-center justify-center px-6 py-24 text-center sm:py-32","children":[["$","span",null,{"className":"inline-grid place-items-center rounded-full bg-surface text-ink shadow-[0_10px_30px-18px_rgba(27,24,16,0.55)] ring-1 ring-line mb-8","style":{"width":84,"height":84},"aria-hidden":"true","children":["$","svg",null,{"viewBox":"0 0 240 268","width":69,"height":69,"className":"$undefined","fill":"none","aria-hidden":true,"role":"$undefined","aria-label":"$undefined","focusable":"false","children":[false,["$","path",null,{"d":"M52 268 C54 224 84 206 120 206 C156 206 186 224 188 268 Z","fill":"#284633","stroke":"#1B1810","strokeWidth":"3.4","strokeLinejoin":"round"}],["$","path",null,{"d":"M120 198 L104 214 L112 232 L120 224 L128 232 L136 214 Z","fill":"#FBF8F0","stroke":"#1B1810","strokeWidth":"3.2","strokeLinejoin":"round"}],["$","path",null,{"d":"M120 44 C72 44 54 74 54 120 C50 150 56 196 86 200 C100 204 140 204 154 200 C184 196 190 150 186 120 C186 74 168 44 120 44 Z","fill":"#E7E0CE"}],["$","g",null,{"fill":"#E7E0CE","stroke":"#1B1810","strokeWidth":"3.4","strokeLinejoin":"round","children":[["$","circle",null,{"cx":"120","cy":"52","r":"22"}],["$","circle",null,{"cx":"92","cy":"60","r":"20"}],["$","circle",null,{"cx":"148","cy":"60","r":"20"}],["$","circle",null,{"cx":"71","cy":"82","r":"19"}],["$","circle",null,{"cx":"169","cy":"82","r":"19"}],["$","circle",null,{"cx":"59","cy":"110","r":"18"}],["$","circle",null,{"cx":"181","cy":"110","r":"18"}],["$","circle",null,{"cx":"58","cy":"140","r":"17"}],["$","circle",null,{"cx":"182","cy":"140","r":"17"}],["$","circle",null,{"cx":"65","cy":"168","r":"16"}],["$","circle",null,{"cx":"175","cy":"168","r":"16"}],["$","circle",null,{"cx":"80","cy":"190","r":"15"}],["$","circle",null,{"cx":"160","cy":"190","r":"15"}]]}],["$","ellipse",null,{"cx":"120","cy":"120","rx":"42","ry":"47","fill":"#F6E7CF","stroke":"#1B1810","strokeWidth":"3.4"}],["$","g",null,{"fill":"#E7E0CE","stroke":"#1B1810","strokeWidth":"3.4","strokeLinejoin":"round","children":[["$","circle",null,{"cx":"105","cy":"74","r":"16"}],["$","circle",null,{"cx":"135","cy":"74","r":"16"}],["$","circle",null,{"cx":"85","cy":"80","r":"16"}],["$","circle",null,{"cx":"155","cy":"80","r":"16"}],["$","circle",null,{"cx":"69","cy":"104","r":"15"}],["$","circle",null,{"cx":"171","cy":"104","r":"15"}],["$","circle",null,{"cx":"66","cy":"140","r":"14"}],["$","circle",null,{"cx":"174","cy":"140","r":"14"}]]}],["$","g",null,{"fill":"none","stroke":"#1B1810","strokeWidth":"2","strokeLinecap":"round","opacity":"0.5","children":[["$","path",null,{"d":"M120 48 a7 7 0 1 1 -5 4"}],["$","path",null,{"d":"M71 82 a6 6 0 1 1 -4 3"}],["$","path",null,{"d":"M169 82 a6 6 0 1 0 4 3"}],["$","path",null,{"d":"M58 140 a5 5 0 1 1 -3 3"}],["$","path",null,{"d":"M182 140 a5 5 0 1 0 3 3"}]]}],["$","path",null,{"d":"M98 104 Q108 99 117 103","fill":"none","stroke":"#1B1810","strokeWidth":"3.4","strokeLinecap":"round"}],["$","path",null,{"d":"M123 103 Q132 99 142 104","fill":"none","stroke":"#1B1810","strokeWidth":"3.4","strokeLinecap":"round"}],["$","ellipse",null,{"cx":"106","cy":"116","rx":"3.6","ry":"5","fill":"#1B1810"}],["$","ellipse",null,{"cx":"134","cy":"116","rx":"3.6","ry":"5","fill":"#1B1810"}],["$","circle",null,{"cx":"107.4","cy":"114","r":"1.3","fill":"#FBF8F0"}],["$","circle",null,{"cx":"135.4","cy":"114","r":"1.3","fill":"#FBF8F0"}],"$L10","$L11","$L12","$L13","$L14","$L15","$L16","$L17","$L18"]}]}],"$L19","$L1a","$L1b","$L1c"]}]}],"$L1d"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}],"$L1e"]}]\n"]) API – Developer Docs | Pepys\",\"description\":\"Programmatic audio, video, and podcast transcription. API keys, a single transcriptions endpoint, podcast RSS support, direct uploads, and signed webhooks.\",\"inLanguage\":\"en\",\"isPartOf\":{\"@id\":\"https://pepys.co/#website\"},\"publisher\":{\"@id\":\"https://pepys.co/#organization\"},\"author\":{\"@id\":\"https://pepys.co/#organization\"}}"}}],["$","$Lf",null,{}],["$","main",null,{"className":"mx-auto w-full max-w-3xl flex-1 px-4 py-14 sm:px-6","children":[["$","p",null,{"className":"font-mono text-[11px] uppercase tracking-widest text-muted","children":"API"}],["$","h1",null,{"className":"mt-2 font-display text-4xl font-semibold tracking-tight text-ink","children":"Transcription API"}],["$","p",null,{"className":"mt-3 text-lg leading-relaxed text-muted","children":"Transcribe any audio, video, or podcast programmatically – over the REST API, or as an MCP server so AI agents transcribe on their own. Async jobs with polling or webhooks, and native podcast (RSS / Apple) support."}],["$","nav",null,{"className":"mt-6 flex flex-wrap gap-x-4 gap-y-1 text-sm text-accent","children":[["$","a","mcp",{"href":"#mcp","className":"underline-offset-2 hover:underline","children":"MCP (AI agents)"}],["$","a","auth",{"href":"#auth","className":"underline-offset-2 hover:underline","children":"Authentication"}],["$","a","create",{"href":"#create","className":"underline-offset-2 hover:underline","children":"Create a transcription"}],["$","a","get",{"href":"#get","className":"underline-offset-2 hover:underline","children":"Get a transcription"}],["$","a","podcasts",{"href":"#podcasts","className":"underline-offset-2 hover:underline","children":"Podcasts"}],["$","a","uploads",{"href":"#uploads","className":"underline-offset-2 hover:underline","children":"Direct uploads"}],["$","a","webhooks",{"href":"#webhooks","className":"underline-offset-2 hover:underline","children":"Webhooks"}],["$","a","errors",{"href":"#errors","className":"underline-offset-2 hover:underline","children":"Errors \u0026 limits"}]]}],["$","div",null,{"className":"mt-10 space-y-10","children":[["$","section",null,{"id":"mcp","className":"scroll-mt-24 border-t border-line pt-10","children":[["$","h2",null,{"className":"font-display text-2xl font-semibold tracking-tight text-ink","children":"MCP – give an AI agent Pepys"}],["$","div",null,{"className":"mt-3 space-y-3 text-[15px] leading-relaxed text-muted","children":[["$","p",null,{"children":["Want the short version? See the"," ",["$","$L1f",null,{"href":"/mcp","className":"text-accent underline-offset-2 hover:underline","children":"MCP server page"}],". Full setup below."]}],["$","p",null,{"children":"Let an AI agent (Claude, ChatGPT, Cursor, and other MCP clients) transcribe on its own – hours-long files, diarization, correctly-timed SRT/VTT, paste-a-link, whole podcast feeds, and search across a transcript – over the Model Context Protocol. Two ways to connect:"}],["$","p",null,{"className":"text-ink","children":"1. Hosted connector – no API key"}],["$","p",null,{"children":["Point a remote MCP connector at"," ",["$","code",null,{"className":"rounded bg-band px-1 py-0.5 font-mono text-xs","children":"https://pepys.co/api/mcp"}],". The agent signs in to Pepys once (OAuth); transcription bills to your credits. In Claude: Settings → Connectors → Add custom connector. Best for Claude and ChatGPT."]}],["$","p",null,{"className":"text-ink","children":"2. Local server – your own key"}],["$","p",null,{"children":["Run the published ",["$","code",null,{"className":"font-mono text-xs","children":"pepys-mcp"}],"server with your API key – best for local dev agents. Add to your client's MCP config:"]}],"$L20","$L21","$L22"]}]]}],"$L23","$L24","$L25","$L26","$L27","$L28","$L29"]}]]}],"$L2a"],["$L2b","$L2c"],"$L2d"]}]\n"]) underline-offset-2 hover:underline","children":"Smithery"}],","," ",["$","a",null,{"href":"https://mcp.so","className":"text-accent underline-offset-2 hover:underline","children":"mcp.so"}],", and the"," ",["$","a",null,{"href":"https://registry.modelcontextprotocol.io","className":"text-accent underline-offset-2 hover:underline","children":"MCP Registry"}],"."]}]\n23:["$","section",null,{"id":"auth","className":"scroll-mt-24 border-t border-line pt-10","children":[["$","h2",null,{"className":"font-display text-2xl font-semibold tracking-tight text-ink","children":"Authentication"}],["$","div",null,{"className":"mt-3 space-y-3 text-[15px] leading-relaxed text-muted","children":[["$","p",null,{"children":["Create a key under"," ",["$","$L1f",null,{"href":"/settings","className":"text-accent underline-offset-2 hover:underline","children":"Settings → API keys"}]," ","(shown once). Send it as a bearer token. All endpoints are under"," ",["$","code",null,{"className":"rounded bg-band px-1 py-0.5 font-mono text-xs","children":"https://pepys.co/api/v1"}],"."]}],["$","pre",null,{"className":"mt-3 overflow-x-auto rounded-xl border border-line bg-band p-4 font-mono text-[13px] leading-relaxed text-ink","children":["$","code",null,{"children":"curl https://pepys.co/api/v1/transcriptions \\\n -H \"Authorization: Bearer pk_live_your_key\""}]}],["$","p",null,{"children":"Usage draws from your account credit balance (1 credit = 1 minute). Enable auto-reload for unattended jobs so they don't pause at a zero balance."}]]}]]}]\n"]) \\\n -H \"Authorization: Bearer pk_live…\" \\\n -H \"Idempotency-Key: my-unique-id\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"url\": \"https://example.com/episode.mp3\",\n \"diarize\": true,\n \"summary\": true,\n \"language\": \"en\"\n }'\n\n# → 202 { \"id\": \"…\", \"status\": \"queued\", \"url\": \"/api/v1/transcriptions/…\" }"}]}],["$","p",null,{"className":"text-ink","children":"Options"}],["$","ul",null,{"className":"list-disc space-y-1 pl-5","children":[["$","li",null,{"children":[["$","code",null,{"className":"font-mono text-xs","children":"language"}]," – ISO code; omit to auto-detect."]}],["$","li",null,{"children":[["$","code",null,{"className":"font-mono text-xs","children":"diarize"}]," – detect speakers (billed at 3× on long-form)."]}],["$","li",null,{"children":[["$","code",null,{"className":"font-mono text-xs","children":"summary"}],", ",["$","code",null,{"className":"font-mono text-xs","children":"chapters"}]," – AI extras."]}],["$","li",null,{"children":[["$","code",null,{"className":"font-mono text-xs","children":"translate_to"}]," – ISO code to also return a translation."]}],["$","li",null,{"children":[["$","code",null,{"className":"font-mono text-xs","children":"quality"}]," – ",["$","code",null,{"className":"font-mono text-xs","children":"fast"}]," | ",["$","code",null,{"className":"font-mono text-xs","children":"accurate"}],"."]}]]}],["$","p",null,{"children":["Reuse an ",["$","code",null,{"className":"font-mono text-xs","children":"Idempotency-Key"}]," to make retries safe. A cached or caption-backed source returns ",["$","code",null,{"className":"font-mono text-xs","children":"200"}]," with"," ",["$","code",null,{"className":"font-mono text-xs","children":"status: \"done\""}]," immediately."]}]]}]]}]\n"])" \\\n -H \"Authorization: Bearer pk_live…\"\n\n# → { \"total\": 2900, \"returned\": 3, \"data\": [ { \"title\", \"guid\", \"published_at\", \"duration_seconds\", \"audio_url\" } ] }\n\n# transcribe a specific episode by guid (no need to pass the audio_url):\ncurl -X POST https://pepys.co/api/v1/transcriptions \\\n -H \"Authorization: Bearer pk_live…\" \\\n -d '{ \"url\": \"\u003cfeed_url\u003e\", \"episode_guid\": \"\u003cguid\u003e\" }'\n# (or \"episode_index\": 0 for the newest, 1 for the next, …)"}]}],["$","p",null,{"className":"text-ink","children":"Transcribe a whole feed"}],["$","p",null,{"children":"Fan a feed out into one transcription per episode (grouped as a batch)."}],["$","p",null,{"className":"font-mono text-sm","children":[["$","span",null,{"className":"rounded bg-accent/10 px-1.5 py-0.5 font-semibold text-accent","children":"POST"}]," ",["$","span",null,{"className":"text-ink","children":"/api/v1/podcasts/transcribe"}]]}],["$","pre",null,{"className":"mt-3 overflow-x-auto rounded-xl border border-line bg-band p-4 font-mono text-[13px] leading-relaxed text-ink","children":["$","code",null,{"children":"curl -X POST https://pepys.co/api/v1/podcasts/transcribe \\\n -H \"Authorization: Bearer pk_live…\" \\\n -d '{ \"feed\": \"\u003cfeed_or_apple_url\u003e\", \"episodes\": \"all\", \"diarize\": true }'\n# episodes: \"latest\" (default) | \"all\" | \u003cnumber\u003e\n# → 202 { \"batch_id\": \"…\", \"total\": 42, \"transcriptions\": [ { \"id\", \"title\", \"guid\", \"url\" } ] }"}]}],["$","p",null,{"children":"Each returned transcription id is polled (or webhook-notified) like any other."}]]}]]}]\n"]) \\\n -H \"Authorization: Bearer pk_live…\" \\\n -d '{ \"filename\": \"call.mp3\", \"content_type\": \"audio/mpeg\", \"bytes\": 5242880 }'\n# → { \"upload_url\": \"https://…\", \"media_ref\": \"uploads/\u003cyou\u003e/\u003cuuid\u003e.mp3\", \"expires_in\": 600 }\n\n# 2. upload the bytes (Content-Type + Content-Length must match)\ncurl -X PUT \"\u003cupload_url\u003e\" -H \"Content-Type: audio/mpeg\" --data-binary @call.mp3\n\n# 3. transcribe it\ncurl -X POST https://pepys.co/api/v1/transcriptions \\\n -H \"Authorization: Bearer pk_live…\" \\\n -d '{ \"media_ref\": \"uploads/\u003cyou\u003e/\u003cuuid\u003e.mp3\" }'"}]}]]}]]}]\n"]): transcription.completed\nPepys-Event-Id: \u003cjobId\u003e.done\nPepys-Signature: t=1718800000,v1=\u003chmac-sha256-hex\u003e\n\n{\n \"id\": \"\u003cjobId\u003e.done\",\n \"type\": \"transcription.completed\",\n \"created\": 1718800000,\n \"data\": { \"transcription\": { \"id\": \"…\", \"status\": \"done\", \"url\": \"https://pepys.co/api/v1/transcriptions/…\" } }\n}"}]}],["$","p",null,{"className":"text-ink","children":"Verify the signature"}],["$","p",null,{"children":["Recompute HMAC-SHA256 over ",["$","code",null,{"className":"font-mono text-xs","children":"{timestamp}.{rawBody}"}]," with your endpoint secret and constant-time compare against the ",["$","code",null,{"className":"font-mono text-xs","children":"v1"}]," value."]}],["$","pre",null,{"className":"mt-3 overflow-x-auto rounded-xl border border-line bg-band p-4 font-mono text-[13px] leading-relaxed text-ink","children":["$","code",null,{"children":"import { createHmac, timingSafeEqual } from \"node:crypto\";\n\nfunction verify(rawBody, header, secret) {\n const { t, v1 } = Object.fromEntries(header.split(\",\").map((p) =\u003e p.split(\"=\")));\n const expected = createHmac(\"sha256\", secret).update(${t}.${rawBody}).digest(\"hex\");\n const ok = v1.length === expected.length \u0026\u0026\n timingSafeEqual(Buffer.from(v1), Buffer.from(expected));\n if (!ok) throw new Error(\"bad signature\");\n // Optional: reject if Math.abs(Date.now()/1000 - Number(t)) \u003e 300 (replay window).\n return JSON.parse(rawBody);\n}"}]}],["$","p",null,{"children":["Respond ",["$","code",null,{"className":"font-mono text-xs","children":"2xx"}]," within ",10,"s. Non-2xx / timeouts retry with backoff (up to 6 attempts). The payload carries a summary; fetch the full transcript via"," ",["$","code",null,{"className":"font-mono text-xs","children":"GET /api/v1/transcriptions/{id}"}],"."]}]]}]]}]\n"]), follow"}],["$","meta","8",{"name":"googlebot","content":"index, follow, max-video-preview:-1, max-image-preview:large, max-snippet:-1"}],["$","link","9",{"rel":"canonical","href":"https://pepys.co/developers"}],["$","meta","10",{"name":"format-detection","content":"telephone=no, address=no, email=no"}],["$","meta","11",{"property":"og:title","content":"Transcription API – Developer Docs | Pepys"}],["$","meta","12",{"property":"og:description","content":"Programmatic audio, video, and podcast transcription. API keys, a single transcriptions endpoint, podcast RSS support, direct uploads, and signed webhooks."}],["$","meta","13",{"property":"og:url","content":"https://pepys.co/developers"}],["$","meta","14",{"property":"og:site_name","content":"Pepys"}],["$","meta","15",{"property":"og:locale","content":"en_US"}],["$","meta","16",{"property":"og:image","content":"https://pepys.co/opengraph-image"}],["$","meta","17",{"property":"og:image:type","content":"image/png"}],["$","meta","18",{"property":"og:image:width","content":"1200"}],["$","meta","19",{"property":"og:image:height","content":"630"}],["$","meta","20",{"property":"og:image:alt","content":"Pepys – transcribe audio and video to text, pay as you go"}],["$","meta","21",{"property":"og:type","content":"website"}],["$","meta","22",{"name":"twitter:card","content":"summary_large_image"}],["$","meta","23",{"name":"twitter:title","content":"Transcription API – Developer Docs | Pepys"}],["$","meta","24",{"name":"twitter:description","content":"Programmatic audio, video, and podcast transcription. API keys, a single transcriptions endpoint, podcast RSS support, direct uploads, and signed webhooks."}],["$","meta","25",{"name":"twitter:image","content":"https://pepys.co/opengraph-image"}],["$","meta","26",{"name":"twitter:image:alt","content":"Pepys – transcribe audio and video to text, pay as you go"}],["$","meta","27",{"name":"twitter:image:type","content":"image/png"}],["$","meta","28",{"name":"twitter:image:width","content":"1200"}],["$","meta","29",{"name":"twitter:image:height","content":"630"}],["$","link","30",{"rel":"icon","href":"/favicon.ico?favicon.21rrjd57gty57.ico?dpl=dpl_8xFFCU88gTSYLbNfJx9g8HgaifDc","sizes":"192x192","type":"image/x-icon"}],["$","link","31",{"rel":"icon","href":"/icon.png?icon.3fc1av3cchcq2.png?dpl=dpl_8xFFCU88gTSYLbNfJx9g8HgaifDc","sizes":"512x512","type":"image/png"}],["$","link","32",{"rel":"icon","href":"/icon.svg?icon.0c71tfnb726pg.svg?dpl=dpl_8xFFCU88gTSYLbNfJx9g8HgaifDc","sizes":"any","type":"image/svg+xml"}],["$","link","33",{"rel":"apple-touch-icon","href":"/apple-icon.png?apple-icon.39wkgwp4xbxbk.png?dpl=dpl_8xFFCU88gTSYLbNfJx9g8HgaifDc","sizes":"180x180","type":"image/png"}],["$","$L38","34",{}]]\n"])